package org.jeecg.modules.networking.service.impl;

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import net.coobird.thumbnailator.Thumbnails;
import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.common.util.RestUtil;
import org.jeecg.modules.carInformationUpload.entity.CarInformationUpload;
import org.jeecg.modules.carRegister.entity.CarRegister;
import org.jeecg.modules.inPlantUpload.entity.InPlantUpload;
import org.jeecg.modules.networking.service.INetWorkingService;
import org.jeecg.modules.networking.util.*;
import org.jeecg.modules.noRoadUpload.entity.NoRoadUpload;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.TimeZone;

@Service
@Slf4j
public class NetWorkingServiceImpl implements INetWorkingService {

    @Value("${networking.baseUrl}")
    private String baseUrl;//台账对接地址
    @Value("${networking.companyCode}")
    private String companyCode;//企业编码
    @Value("${networking.username}")
    private String username;//企业门禁系统联网对接用户名
    @Value("${networking.allocPassword}")
    private String allocPassword;//企业门禁系统联网对接密码
    @Value("${networking.tokenKey}")
    private String tokenKey;//redis中access_token键名
    @Value("${networking.publicKeyKey}")
    private String publicKeyKey;//redis中public_key键名

    @Autowired
    private RedisUtil redisUtil;

    private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(TimeZone.getDefault().toZoneId());

    @Override
    public String getToken() {
        if (redisUtil.hasKey(tokenKey)){
            String token = (String) redisUtil.get(tokenKey);
            if (!StringUtils.isEmpty(token)){
                return token;
            }
        }
        JSONObject jsonObject = updateLocalTokenInfo();
        return jsonObject.getString("access_token");
    }

    @Override
    public String getPublicKey() {
        if (redisUtil.hasKey(publicKeyKey)){
            String publicKey = (String) redisUtil.get(publicKeyKey);
            if (!StringUtils.isEmpty(publicKey)){
                return publicKey;
            }
        }
        JSONObject jsonObject = updateLocalTokenInfo();
        return jsonObject.getString("public_key");
    }

    @Override
    public JSONObject syncInfo() {
        String url = baseUrl + "/api/v1/sync/syncInfo";
        JSONObject request = new JSONObject();
        request.put("company_code", companyCode);
        request.put("access_token", getToken());
        return RestUtil.post(url, request);
    }

    @Override
    public JSONObject putRegisterVehicleInfo(CarRegister carRegister) throws InvalidCipherTextException, IOException {
        String url = baseUrl + "/api/v1/vehicleInfo/putRegisterVehicleInfo";
        JSONObject value = new JSONObject();
        value.put("plateNo", carRegister.getPlateNo());
        value.put("plateColor", carRegister.getPlateColor());
        value.put("vehicleType", VehicleCodeMapper.getVehicleCode(carRegister.getCarType()));
        value.put("vin", carRegister.getVin());
        value.put("engineNo", carRegister.getEngineNum());
        value.put("registerDate", formatDateToString(carRegister.getRegisterDate(), "yyyyMMdd"));
        value.put("vehicleBrand", carRegister.getCarBrandModel());
        value.put("vehicleModel", carRegister.getCarBrandModel());
        value.put("fuelType", carRegister.getFuelType());
        value.put("engineType", carRegister.getEngineType());
        value.put("engineManufacturer", carRegister.getEngineManufacturer());
        value.put("emissionStandard", carRegister.getEvnType());
        value.put("connectedStatus", carRegister.getNetworkingStatus());
        value.put("useCharacter", FunctionMapper.getFunction(carRegister.getFunction()));
        value.put("owner", carRegister.getFleet());
        JSONObject request = new JSONObject();
        request.put("company_code", companyCode);
        request.put("access_token", getToken());
        request.put("encrypt_method", "2");
        request.put("value", encrypt(value));
        String envLicense = carRegister.getInventory();
        String drivingLicense = carRegister.getLicense();
        String envLicenseImg = compressPicForScale(envLicense, 2048);
        String drivingLicenseImg = compressPicForScale(drivingLicense, 2048);
        request.put("envLicenseImg", getSwitch(envLicense, envLicenseImg));
        request.put("drivingLicenseImg", getSwitch(drivingLicense, drivingLicenseImg));
        return RestUtil.post(url, request);
    }

    @Override
    public JSONObject putVehiclePassRecords(CarInformationUpload carInformationUpload) throws InvalidCipherTextException, IOException {
        String url = baseUrl + "/api/v1/vehiclePassRecords/putVehiclePassRecords";
        JSONObject value = new JSONObject();
        value.put("serialNo", companyCode + carInformationUpload.getBarrierGateNumber() +
                carInformationUpload.getEntryExitStatus() + formatDateToString(carInformationUpload.getEntryExitTime(), "yyMMddHHmmss"));
        value.put("entranceNo", carInformationUpload.getGateNumber());
        value.put("gateNo", carInformationUpload.getBarrierGateNumber());
        value.put("passStatus", carInformationUpload.getEntryExitStatus());
        value.put("passTime", formatDateToString(carInformationUpload.getEntryExitTime(), "yyyy-MM-dd HH:mm:ss"));
        value.put("plateNo", carInformationUpload.getPlateNo());
        value.put("plateColor", carInformationUpload.getPlateColor());
        value.put("vehicleType", VehicleCodeMapper.getVehicleCode(carInformationUpload.getCarType()));
        value.put("vin", carInformationUpload.getVin());
        value.put("engineNo", carInformationUpload.getEngineNum());
        value.put("registerDate", formatDateToString(carInformationUpload.getRegisterDate(), "yyyyMMdd"));
        value.put("vehicleBrand", carInformationUpload.getCarBrandModel());
        value.put("vehicleModel", carInformationUpload.getCarBrandModel());
        value.put("fuelType", carInformationUpload.getFuelType());
        value.put("emissionStandard", carInformationUpload.getEvnType());
        value.put("connectedStatus", carInformationUpload.getNetworkingStatus());

        value.put("handMove", "0");
        value.put("useCharacter", FunctionMapper.getFunction(carInformationUpload.getFunction()));
        value.put("transportGoods", carInformationUpload.getGoods());
        value.put("transportGoodType", GoodsMapper.getGoodsType(carInformationUpload.getGoods()));
        value.put("transportVolume", carInformationUpload.getFreightVolume());
        value.put("transportGoodsUnit", "吨（t）");
        value.put("dataComment", "");

        JSONObject request = new JSONObject();
        request.put("company_code", companyCode);
        request.put("access_token", getToken());
        request.put("encrypt_method", "2");
        request.put("value", encrypt(value));
        String vehicleHead = carInformationUpload.getEntryExitPic();
        String vehicleBody = carInformationUpload.getHighPic();
        String vehicleHeadImg = compressPicForScale(vehicleHead, 2048);
        String vehicleBodyImg = compressPicForScale(vehicleBody, 2048);
        request.put("vehicleHeadImg", getSwitch(vehicleHead, vehicleHeadImg));
        request.put("vehicleBodyImg", getSwitch(vehicleBody, vehicleBodyImg));
        log.info("value : {}", value);
        return RestUtil.post(url, request);
    }

    @Override
    public JSONObject putVehicleInfo(InPlantUpload inPlantUpload) throws InvalidCipherTextException, IOException {
        String url = baseUrl + "/api/v1/innerVehicle/putVehicleInfo";
        JSONObject value = new JSONObject();
        value.put("envRegisterCode", inPlantUpload.getRegistrationCode());
        value.put("provRegisterCode", inPlantUpload.getRegistrationCode());
        value.put("vin", inPlantUpload.getVin());
        value.put("manufactureDate", formatDateToString(inPlantUpload.getRegisterDate(), "yyyyMMdd"));
        value.put("plateNo", inPlantUpload.getPlateNo());
        value.put("registerDate", "");
        value.put("vehicleBrand", inPlantUpload.getCarBrandModel());
        value.put("vehicleModel", inPlantUpload.getCarBrandModel());
        value.put("fuelType", inPlantUpload.getFuelType());
        value.put("emissionStandard", inPlantUpload.getEvnType());
        value.put("connectedStatus", inPlantUpload.getNetworkingStatus());
        value.put("owner", inPlantUpload.getVehicleOwner());
        JSONObject request = new JSONObject();
        request.put("company_code", companyCode);
        request.put("access_token", getToken());
        request.put("encrypt_method", "2");
        request.put("value", encrypt(value));
        String envLicense = inPlantUpload.getInventory();
        String drivingLicense = inPlantUpload.getLicense();
        String envLicenseImg = compressPicForScale(envLicense, 2048);
        String drivingLicenseImg = compressPicForScale(drivingLicense, 2048);
        request.put("envLicenseImg", getSwitch(envLicense, envLicenseImg));
        request.put("drivingLicenseImg", getSwitch(drivingLicense, drivingLicenseImg));
        return RestUtil.post(url, request);
    }

    @Override
    public JSONObject putRegisterNRMM(NoRoadUpload noRoadUpload) throws InvalidCipherTextException, IOException {
        String url = baseUrl + "/api/v1/nrmm/putRegisterNRMM";
        JSONObject value = new JSONObject();
        value.put("envRegisterCode", noRoadUpload.getRegistrationCode());
        value.put("provRegisterCode", noRoadUpload.getRegistrationCode());
        value.put("manufactureDate", formatDateToString(noRoadUpload.getProducedTime(), "yyyyMMdd"));
        value.put("plateNo", noRoadUpload.getPlateNo());
        value.put("fuelType", noRoadUpload.getFuelType());
        value.put("emissionStandard", noRoadUpload.getEvnType());

        value.put("mechanicalType", noRoadUpload.getMachineryType());
        value.put("pin", noRoadUpload.getPin());
        value.put("mechanicalModel", noRoadUpload.getMechanicalModel());
        value.put("engineModel", noRoadUpload.getEngineModel());
        value.put("engineManufacture", noRoadUpload.getEngineProducer());
        value.put("engineNo", noRoadUpload.getEngineNum());
        value.put("onwer", noRoadUpload.getVehicleOwner());
        JSONObject request = new JSONObject();
        request.put("company_code", companyCode);
        request.put("access_token", getToken());
        request.put("encrypt_method", "2");
        request.put("value", encrypt(value));
        String nameplate = noRoadUpload.getMachineNameplate();
        String engineNameplate = noRoadUpload.getEngineNameplate();
        String mechineEnvLabel = noRoadUpload.getMachineLabel();
        String nameplateImg = compressPicForScale(nameplate, 2048);
        String engineNameplateImg = compressPicForScale(engineNameplate, 2048);
        String mechineEnvLabelImg = compressPicForScale(engineNameplate, 2048);
        request.put("nameplateImg", getSwitch(nameplate, nameplateImg));
        request.put("engineNameplateImg", getSwitch(engineNameplate, engineNameplateImg));
        request.put("mechineEnvLabelImg", getSwitch(mechineEnvLabel, mechineEnvLabelImg));
        return RestUtil.post(url, request);
    }

    @Override
    public JSONObject putVehicleCheckResult() throws InvalidCipherTextException {
        return null;
    }

    @Override
    public JSONObject putVehiclePassRecordsNum(Date day, int recordsNumber) throws InvalidCipherTextException {
        String url = baseUrl + "/api/v1/vehiclePassRecords/putVehiclePassRecordsNum";
        JSONObject value = new JSONObject();
        value.put("day", formatDateToString(day, "yyyyMMdd"));
        value.put("entranceNo", "A");
        value.put("gateNo", "A01");
        value.put("recordsNumber", recordsNumber);
        JSONObject request = new JSONObject();
        request.put("company_code", companyCode);
        request.put("access_token", getToken());
        request.put("encrypt_method", "2");
        request.put("value", encrypt(value));
        return RestUtil.post(url, request);
    }

    @Override
    public JSONObject breakPassRules() {
        String url = baseUrl + "/api/v1/vehicleBlacklist/breakPassRules/getVehicleInfos";
        JSONObject request = new JSONObject();
        request.put("company_code", companyCode);
        request.put("access_token", getToken());
        return RestUtil.post(url, request);
    }

    @Override
    public JSONObject breakEnvRules() {
        String url = baseUrl + "/api/v1/vehicleBlacklist/breakEnvRules/getVehicleInfos";
        JSONObject request = new JSONObject();
        request.put("company_code", companyCode);
        request.put("access_token", getToken());
        return RestUtil.post(url, request);
    }

    @Override
    public JSONObject getControlStrategy() {
        String url = baseUrl + "/api/v1/VehicleControlStrategy/getControlStrategy";
        JSONObject request = new JSONObject();
        request.put("company_code", companyCode);
        request.put("access_token", getToken());
        return RestUtil.post(url, request);
    }

    /**
     * 从服务器获取访问令牌
     * @return 包含访问令牌的JSON对象
     */
    private JSONObject getAccessToken() {
        String url = baseUrl + "/api/v1/token/getAccessToken";
        JSONObject request = new JSONObject();
        request.put("username", username);
        request.put("encrypt_method", 2);
        LocalDateTime localDateTime = LocalDateTime.now().plusSeconds(30);
        long timestamp = localDateTime.toEpochSecond(ZoneOffset.UTC);
        request.put("requestTimestamp", timestamp);
        // 构建待加密字符串
        String toEncrypt = username + timestamp + allocPassword;
        // 执行SM3加密
        String encryptedPassword = SM3Util.encryptSM3(toEncrypt);
        request.put("password", encryptedPassword);
        try {
            return RestUtil.post(url, request);
        } catch (Exception e) {
            throw new RuntimeException("从服务器获取token失败", e);
        }
    }

    /**
     * 更新本地的访问令牌和公钥信息
     * @return 包含访问令牌的JSON对象
     */
    @Override
    public JSONObject updateLocalTokenInfo() {
        JSONObject accessToken = getAccessToken();
        if (accessToken != null && "200".equals(accessToken.getString("code"))) {
            String token = accessToken.getString("access_token");
            String publicKey = accessToken.getString("public_key");
            String pkExpiryTime = accessToken.getString("pk_expiry_time");
            String datetime = accessToken.getString("datetime");
            // 计算过期时间差
            LocalDateTime expiryTime = LocalDateTime.parse(pkExpiryTime, formatter);
            LocalDateTime serverTime = LocalDateTime.parse(datetime, formatter);
            long expireInSeconds = Duration.between(serverTime, expiryTime).getSeconds();
            // 存储到Redis
            redisUtil.set(tokenKey, token, 60 * 55 * 2);
            redisUtil.set(publicKeyKey, publicKey, expireInSeconds);
            return accessToken;
        }
        throw new RuntimeException("获取token失败");
    }

    private String encrypt(JSONObject jsonObject) throws InvalidCipherTextException {

        String jsonString = jsonObject.toJSONString();
        return SM2Utils.encrypt(getPublicKey(), jsonString);
    }

    /**
     * 根据指定大小压缩图片
     *
     * @param imagePath  源图片地址（网络地址）
     * @param desFileSize 指定图片大小，单位kb
     * @return 压缩质量后的图片字节数组
     */
    public static String compressPicForScale(String imagePath, long desFileSize) throws IOException {
        byte[] data = getBytesFromHost(imagePath);

        if (data.length < desFileSize * 1024) {
            return Base64.encodeBase64String(data);
        }

        long srcSize = data.length;
        double accuracy = getAccuracy(srcSize / 1024);

        try {
            while (data.length > desFileSize * 1024) {
                try (ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
                     ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length)) {
                    Thumbnails.of(inputStream)
                            .scale(accuracy)
                            .outputQuality(accuracy)
                            .toOutputStream(outputStream);
                    data = outputStream.toByteArray();
                }
            }
            log.info("图片原大小={}kb | 压缩后大小={}kb", srcSize / 1024, data.length / 1024);
        } catch (Exception e) {
            log.error(String.format("【图片压缩】msg=图片压缩失败! imagePath=%s, desFileSize=%d", imagePath, desFileSize));
            throw e; // 重新抛出异常以便调用者处理
        }

        return Base64.encodeBase64String(data);
    }

    public static byte[] getBytesFromHost(String imagePath) throws IOException {
        try (InputStream in = new URL(imagePath).openStream();
             ByteArrayOutputStream data = new ByteArrayOutputStream()) {
            final byte[] buffer = new byte[4096]; // 增加缓冲区大小
            int bytesRead;
            while ((bytesRead = in.read(buffer)) != -1) {
                data.write(buffer, 0, bytesRead);
            }
            return data.toByteArray();
        }
    }

    /**
     * 自动调节精度(经验数值)
     *
     * @param size 源图片大小
     * @return 图片压缩质量比
     */
    private static double getAccuracy(long size) {
        double accuracy;
        if (size < 900) {
            accuracy = 0.85;
        } else if (size < 2047) {
            accuracy = 0.6;
        } else if (size < 3275) {
            accuracy = 0.44;
        } else {
            accuracy = 0.4;
        }
        return accuracy;
    }

    /** *filename文件名  base64 */
    public static String getSwitch(String filename,String base64) {
        String str = filename.substring(filename.lastIndexOf(".") + 1);
        System.out.println(str);
        //png
        String pngPrefix = "data:image/png;base64,";
        //jpg
        String jpegPrefix = "data:image/jpeg;base64,";
        //docx
        String docxPrefix = "data:application/vnd.openxmlformats-officedocument.wordprocessingml.document ;base64,";
        //doc
        String docPrefix = "data:application/msword;base64,";
        //pdf
        String pdfPrefix = "data:application/pdf;base64,";
        String filebase64 = null;
        if (com.baomidou.mybatisplus.core.toolkit.StringUtils.isNotBlank(str)) {
            switch (str) {
                case "png":
                    filebase64 = pngPrefix.concat(base64);
                    break;
                case "jpg":
                    filebase64 = jpegPrefix.concat(base64);
                    break;
                case "docx":
                    filebase64 = docxPrefix.concat(base64);
                    break;
                case "doc":
                    filebase64 = docPrefix.concat(base64);
                    break;
                case "pdf":
                    filebase64 = pdfPrefix.concat(base64);
                    break;
                default:
                    break;
            }
        }
        return  filebase64;
    }

    /**
     * 将Date类型的日期转换为字符串。
     *
     * @param date 要转换的日期
     * @param pattern 要转换成的格式
     * @return 格式化后的日期字符串
     */
    public static String formatDateToString(Date date, String pattern) {
        SimpleDateFormat sdf = new SimpleDateFormat(pattern);// 创建一个SimpleDateFormat对象，并指定日期/时间格式
        return sdf.format(date);// 使用SimpleDateFormat对象的format方法将Date对象转换为字符串
    }

}
